Ca limbaj orientat pe obiecte, C# acceptă conceptele de încapsulare, moștenire și polimorfism. O clasă poate moșteni direct de la o clasă părinte și poate implementa orice număr de interfețe. Metodele care suprascriu metodele virtuale dintr-o clasă părinte necesită cuvântul cheie override ca o modalitate de a evita redefinirea accidentală. În C#, o structură este ca o clasă simplificată; este un tip alocat în stivă care poate implementa interfețe, dar nu acceptă moștenirea. C# oferă clase de tip record și structuri de tip record, care sunt tipuri al căror scop este în primul rând stocarea valorilor datelor.
Clasele sunt cele mai importante dintre tipurile C#. O clasă este o structură de date care combină stările (câmpuri) și acțiunile (metode și alți membri ai funcției) într-o singură unitate. O clasă oferă o definiție pentru instanțe ale clasei, cunoscute și ca obiecte. Clasele suportă moștenirea și polimorfismul, mecanisme prin care clasele derivate pot extinde și specializa clasele de bază.
Clasele noi sunt create folosind declarații de clasă. O declarație de clasă începe cu un antet. Antetul specifică:
Atributele și modificatorii clasei
Numele clasei
Clasa de bază (când moșteniți de la o clasă de bază)
Interfețele implementate de clasă.
Antetul este urmat de corpul clasei, care constă dintr-o listă de declarații de membri scrise între delimitatorii { and }.
Următorul cod arată o declarație a unei clase simple numite Point:
public class Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); }
Instanțele claselor sunt create folosind operatorul new, care alocă memorie pentru o instanță nouă, invocă un constructor pentru a inițializa instanța și returnează o referință la instanță. Următoarele instrucțiuni creează două obiecte Point și stochează referințe la acele obiecte în două variabile:
var p1 = new Point(0, 0); var p2 = new Point(10, 20);
Memoria ocupată de un obiect este recuperată automat atunci când obiectul nu mai este accesibil. Nu este necesar sau posibil să dezalocați în mod explicit obiectele în C#.
Clasele generice definesc parametrii type. Parametrii type sunt o listă de nume de parametri de tip cuprinse între paranteze unghiulare. Parametrii type urmează numele clasei. Parametrii type pot fi apoi utilizați în corpul declarațiilor de clasă pentru a defini membrii clasei. În exemplul următor, parametrii type ai clasei Pair sunt TFirst și TSecond:
public class Pair{ public TFirst First { get; } public TSecond Second { get; } public Pair(TFirst first, TSecond second) => (First, Second) = (first, second); }
O clasă type care este declarată să accepte parametrii type se numește tip de clasă generică. Type-urile de structură, interfață și delegat pot fi, de asemenea, generice. Când se utilizează clasa generică, trebuie furnizate argumente type pentru fiecare dintre parametrii type:
var pair = new Pair(1, "two"); int i = pair.First; //TFirst int string s = pair.Second; //TSecond string
Un type generic cu argumente type furnizate, cum ar fi Pair
O declarație de clasă poate specifica o clasă de bază. Urmați numele clasei și parametrii type cu două puncte și numele clasei de bază. Omiterea unei specificații a clasei de bază este aceeași cu derivarea din tipul obiectului. În exemplul următor, clasa de bază a Point3D este Point. Din primul exemplu, clasa de bază a Point este obiect:
O clasă moștenește membrii clasei sale de bază. Moștenirea înseamnă că o clasă conține implicit aproape toți membrii clasei sale de bază. O clasă nu moștenește instanța și constructorii statici și finalizatorul. O clasă derivată poate adăuga noi membri acelor membri pe care îi moștenește, dar nu poate elimina definiția unui membru moștenit. În exemplul anterior, Point3D moștenește membrii X și Y de la Point și fiecare instanță Point3D conține trei proprietăți, X, Y și Z.
Există o conversie implicită de la type de clasă la oricare dintre type-urile sale de clasă de bază. O variabilă a unui type de clasă poate face referire la o instanță a acelei clase sau la o instanță a oricărei clase derivate. De exemplu, având în vedere declarațiile anterioare de clasă, o variabilă type Point poate face referire fie la un Point, fie la un Point3D:
O interfață definește un contract care poate fi implementat prin clase și structuri. Definim o interfață pentru a declara capabilitățile care sunt partajate între type-uri distincte. De exemplu, interfața System.Collections.Generic.IEnumerable
Interfețele pot folosi moșteniri multiple. În exemplul următor, interfața IComboBox moștenește atât de la ITextBox, cât și de la IListBox.
Clasele și structurile pot implementa mai multe interfețe. În exemplul următor, clasa EditBox implementează atât IControl, cât și IDataBound.
Când o clasă sau o structură implementează o anumită interfață, instanțe ale acelei clase sau structuri pot fi implicit convertite la acel type de interfață. De exemplu:
Un tip Enum definește un set de valori constante. Următoarea enumerare declară constante care definesc diferite legume rădăcinoase:
De asemenea, puteți defini o enumerare care să fie utilizată în combinație ca flaguri. Următoarea declarație declară un set de flaguri pentru cele patru sezoane de anotimpuri. Se poate aplica orice combinație de anotimpuri, inclusiv o valoare All care include toate anotimpurile:
Variabilele de orice tip pot fi declarate ca non-nullable sau nullable. O variabilă nulă poate deține o valoare nulă suplimentară, indicând nicio valoare. Tipurile de valori nullabile (structuri sau enumerari) sunt reprezentate de System.Nullable
C# acceptă tupluri, care oferă o sintaxă concisă pentru a grupa mai multe elemente de date într-o structură de date ușoară. Instanțiem un tuplu declarând tipurile și numele membrilor între (and), așa cum se arată în exemplul următor:
Tuplurile oferă o alternativă pentru structura de date cu mai mulți membri, fără a utiliza blocurile descrise în articolul următor.
TODO
[1] Microsoft Corporation. C# Documentation, https://docs.microsoft.com/en-us/dotnet/csharp/, 2022.
2.3 Clase de bază
public class Point3D : Point
{
public int Z { get; set; }
public Point3D(int x, int y, int z) : base(x, y)
{
Z = z;
}
}
Point a = new(10, 20);
Point b = new Point3D(10, 20, 30);
2.4 Structuri
Clasele definesc type-uri care suportă moștenirea și polimorfismul. Acestea vă permit să creați comportamente sofisticate bazate pe ierarhii de clase derivate. Prin contrast, type-urile struct sunt type-uri mai simple al căror scop principal este stocarea valorilor datelor. Structurile nu pot declara un type de bază; ele derivă implicit din System.ValueType. Nu puteți deriva alte type-uri de struct dintr-un type struct. Sunt implicit sigilate.
public struct Point
{
public double X { get; }
public double Y { get; }
public Point(double x, double y) => (X, Y) = (x, y);
}
Interfețe
interface IControl
{
void Paint();
}
interface ITextBox : IControl
{
void SetText(string text);
}
interface IListBox : IControl
{
void SetItems(string[] items);
}
interface IComboBox : ITextBox, IListBox { }
interface IDataBound
{
void Bind(Binder b);
}
public class EditBox : IControl, IDataBound
{
public void Paint() { }
public void Bind(Binder b) { }
}
EditBox editBox = new();
IControl control = editBox;
IDataBound dataBound = editBox;
2.5 Enumerări
public enum SomeRootVegetable
{
HorseRadish,
Radish,
Turnip
}
[Flags]
public enum Seasons
{
None = 0,
Summer = 1,
Autumn = 2,
Winter = 4,
Spring = 8,
All = Summer | Autumn | Winter | Spring
}
Următorul exemplu arată declarațiile ambelor enumerări precedente:
var turnip = SomeRootVegetable.Turnip;
var spring = Seasons.Spring;
var startingOnEquinox = Seasons.Spring | Seasons.Autumn;
var theYear = Seasons.All;
2.6 Tipuri nullable
int? optionalInt = default;
optionalInt = 5;
string? optionalText = default;
optionalText = "Hello World.";
2.7 Tupluri
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
//Output:
//Sum of 3 elements is 4.5.
2.8 Sarcini
Bibliografie